/**
 *    Copyright (C) 2009 Victor Smirnov <aist11@gmail.com>
 *
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License along
 *    with this program; if not, write to the Free Software Foundation, Inc.,
 *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 *    See LICENSE.exceptions in the root folder of this project for possible
 *    exceptions
 *
 */

#include <cubefs/core/tools/plugin.hpp>
#include <cubefs/api/tools/plugin.hpp>
#include <cubefs/api/tools/strings.hpp>
#include <cubefs/api/exceptions.hpp>

#include <iostream>
#include <vector>
#include <memory>

#define CUBEFS_SOURCE "api-impl/all/plugin.cpp:" CUBEFS_AT

namespace cubefs{ namespace tools {

using namespace std;
using namespace cubefs::api;

Plugin::Plugin(const File& path): path_(path)
{
	if (path.IsExists())
	{
		if (path.IsDirectory())
		{
			auto_ptr<File::FileListType> list(File::ReadDir(path));

			File* resource 	 = NULL;
			SystemDescriptor sysd = GetSystemDescriptor();

			bool configured = false;
			for (File::FileListType::iterator i = list->begin(); i != list->end(); i++)
			{
				File* file = *i;
				if ((!file->IsDirectory()) && IsEndsWith(file->GetName(), ".props"))
				{
					Configurator::Parse(file->GetPath(), &config_);
					configured = true;
				}
				else {
					//cout<<"File "+file->GetPath()+" doesn't match"<<endl;
				}
			}

			if (!configured)
			{
				throw FileException(CUBEFS_SOURCE, "No descriptor(s) for plugin", path.GetPath());
			}

			for (File::FileListType::iterator i = list->begin(); i != list->end(); i++)
			{
				File* file = *i;
				if (IsEndsWith(file->GetName(), Configurator::GetLibraryExtension()))
				{
					ResourceDescriptor descriptor = Plugin::GetResouerceDescriptor(&config_, *file);

					if (descriptor.plugin_ && descriptor.arch_ == sysd.arch_)
					{
						resource 	= file;
						descriptor_ = descriptor;
						break;
					}
				}
			}

			if (resource != NULL)
			{
				config_.AddProperty("resource", resource->GetPath());
				library_.Setup(resource->GetPath(), false);
			}
			else {
				throw FileException(CUBEFS_SOURCE, "No shared library for plugin", path.GetPath());
			}


		}
		else {
			throw FileException(CUBEFS_SOURCE, "Compound resources are not yet supported", path.GetPath());
		}
	}
	else {
		throw FileException(CUBEFS_SOURCE, "Plugin not found", path.GetPath());
	}
}

Plugin::~Plugin() throw() {

}

Plugin* Plugin::FindPlugin(const Configurator* config, StringRef type, StringRef name, const Version& version, const Version& api_version)
{
	PathList list(config->GetProperty("plugin.search.path"));

	vector<Plugin*> plugins;

	SystemDescriptor sysd = GetSystemDescriptor();

	for (Int c = 0; c < list.Size(); c++)
	{
		try {
			String dirPath = list.GetItem(c);
			File path(dirPath);
			if (path.IsExists())
			{
				if (path.IsDirectory())
				{
					auto_ptr<File::FileListType> plugin_list(File::ReadDir(path));
					for (File::FileListType::iterator i = plugin_list->begin(); i != plugin_list->end(); i++)
					{
						File *file = *i;
						if (file->IsDirectory())
						{
							try {
								auto_ptr<Plugin> plugin(new Plugin(*file));

//								cout<<"Plugin: "<<plugin->GetConfig().GetProperty("resource")<<" "<<type<<" "<<name<<endl;
//								cout<<"type\t\t= "<<plugin->GetConfig().GetProperty("type")<<endl;
//								cout<<"name\t\t= "<<plugin->GetConfig().GetProperty("name")<<endl;
//								cout<<"version\t\t= "<<plugin->GetConfig().GetProperty("version")<<endl;
//								cout<<"version.api\t= "<<plugin->GetConfig().GetProperty("version.api")<<endl;

								if (plugin->GetConfig().GetProperty("type") == type &&
									plugin->GetConfig().GetProperty("name") == name
								)
								{
									if (api_version.IsMatches(plugin->GetApiVersion()) &&
										version.IsMatches(plugin->GetVersion()) &&
										plugin->GetResourceDescriptor().arch_ == sysd.arch_)
									{
										plugins.push_back(new Plugin(*plugin));
									}
								}
							}
							catch (CubefsException e) {
								cout<<e.source()<<": "<<e.message()<<endl;
							}
						}
					}
				}
			}
		}
		catch (CubefsException e)
		{
			cout<<e.source()<<": "<<e.message()<<endl;
		}
	}

	Plugin* plugin = NULL;

//	cout<<"found plugins: "<<plugins.size()<<endl;

	for (Int c = 0; c < plugins.size(); c++)
	{
		if (plugin != NULL)
		{
			if (plugins[c]->GetVersion() >= plugin->GetVersion()) {
				plugin = plugins[c];
			}
		}
		else {
			plugin = plugins[c];
		}
	}

	for (Int c = 0; c < plugins.size(); c++)
	{
		if (plugins[c] != plugin)
		{
			delete plugins[c];
		}
	}

	return plugin;
}


}}


namespace cubefs {namespace api {



}}

#undef CUBEFS_SOURCE
